1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.commons.lang3.reflect;
18
19 import static org.hamcrest.Matchers.hasItemInArray;
20 import static org.hamcrest.Matchers.hasItems;
21 import static org.junit.Assert.assertArrayEquals;
22 import static org.junit.Assert.assertEquals;
23 import static org.junit.Assert.assertFalse;
24 import static org.junit.Assert.assertNotNull;
25 import static org.junit.Assert.assertNotSame;
26 import static org.junit.Assert.assertNull;
27 import static org.junit.Assert.assertSame;
28 import static org.junit.Assert.assertThat;
29 import static org.junit.Assert.assertTrue;
30 import static org.junit.Assert.fail;
31
32 import java.lang.reflect.Method;
33 import java.lang.reflect.Type;
34 import java.util.Arrays;
35 import java.util.HashMap;
36 import java.util.Iterator;
37 import java.util.List;
38 import java.util.Map;
39
40 import org.apache.commons.lang3.ArrayUtils;
41 import org.apache.commons.lang3.math.NumberUtils;
42 import org.apache.commons.lang3.mutable.Mutable;
43 import org.apache.commons.lang3.mutable.MutableObject;
44 import org.apache.commons.lang3.ClassUtils.Interfaces;
45 import org.apache.commons.lang3.reflect.testbed.Annotated;
46 import org.apache.commons.lang3.reflect.testbed.GenericConsumer;
47 import org.apache.commons.lang3.reflect.testbed.GenericParent;
48 import org.apache.commons.lang3.reflect.testbed.StringParameterizedChild;
49 import org.junit.Before;
50 import org.junit.Test;
51
52
53
54
55
56 public class MethodUtilsTest {
57
58 private static interface PrivateInterface {}
59
60 static class TestBeanWithInterfaces implements PrivateInterface {
61 public String foo() {
62 return "foo()";
63 }
64 }
65
66 public static class TestBean {
67
68 public static String bar() {
69 return "bar()";
70 }
71
72 public static String bar(final int i) {
73 return "bar(int)";
74 }
75
76 public static String bar(final Integer i) {
77 return "bar(Integer)";
78 }
79
80 public static String bar(final double d) {
81 return "bar(double)";
82 }
83
84 public static String bar(final String s) {
85 return "bar(String)";
86 }
87
88 public static String bar(final Object o) {
89 return "bar(Object)";
90 }
91
92 public static void oneParameterStatic(final String s) {
93
94 }
95
96 @SuppressWarnings("unused")
97 private void privateStuff() {
98 }
99
100
101 public String foo() {
102 return "foo()";
103 }
104
105 public String foo(final int i) {
106 return "foo(int)";
107 }
108
109 public String foo(final Integer i) {
110 return "foo(Integer)";
111 }
112
113 public String foo(final double d) {
114 return "foo(double)";
115 }
116
117 public String foo(final String s) {
118 return "foo(String)";
119 }
120
121 public String foo(final Object o) {
122 return "foo(Object)";
123 }
124
125 public void oneParameter(final String s) {
126
127 }
128 }
129
130 private static class TestMutable implements Mutable<Object> {
131 @Override
132 public Object getValue() {
133 return null;
134 }
135
136 @Override
137 public void setValue(final Object value) {
138 }
139 }
140
141 private TestBean testBean;
142 private final Map<Class<?>, Class<?>[]> classCache = new HashMap<Class<?>, Class<?>[]>();
143
144 @Before
145 public void setUp() throws Exception {
146 testBean = new TestBean();
147 classCache.clear();
148 }
149
150 @Test
151 public void testConstructor() throws Exception {
152 assertNotNull(MethodUtils.class.newInstance());
153 }
154
155 @Test
156 public void testInvokeMethod() throws Exception {
157 assertEquals("foo()", MethodUtils.invokeMethod(testBean, "foo",
158 (Object[]) ArrayUtils.EMPTY_CLASS_ARRAY));
159 assertEquals("foo()", MethodUtils.invokeMethod(testBean, "foo"));
160 assertEquals("foo()", MethodUtils.invokeMethod(testBean, "foo",
161 (Object[]) null));
162 assertEquals("foo()", MethodUtils.invokeMethod(testBean, "foo",
163 (Object[]) null, (Class<?>[]) null));
164 assertEquals("foo(String)", MethodUtils.invokeMethod(testBean, "foo",
165 ""));
166 assertEquals("foo(Object)", MethodUtils.invokeMethod(testBean, "foo",
167 new Object()));
168 assertEquals("foo(Object)", MethodUtils.invokeMethod(testBean, "foo",
169 Boolean.TRUE));
170 assertEquals("foo(Integer)", MethodUtils.invokeMethod(testBean, "foo",
171 NumberUtils.INTEGER_ONE));
172 assertEquals("foo(int)", MethodUtils.invokeMethod(testBean, "foo",
173 NumberUtils.BYTE_ONE));
174 assertEquals("foo(double)", MethodUtils.invokeMethod(testBean, "foo",
175 NumberUtils.LONG_ONE));
176 assertEquals("foo(double)", MethodUtils.invokeMethod(testBean, "foo",
177 NumberUtils.DOUBLE_ONE));
178 }
179
180 @Test
181 public void testInvokeExactMethod() throws Exception {
182 assertEquals("foo()", MethodUtils.invokeExactMethod(testBean, "foo",
183 (Object[]) ArrayUtils.EMPTY_CLASS_ARRAY));
184 assertEquals("foo()", MethodUtils.invokeExactMethod(testBean, "foo"));
185 assertEquals("foo()", MethodUtils.invokeExactMethod(testBean, "foo",
186 (Object[]) null));
187 assertEquals("foo()", MethodUtils.invokeExactMethod(testBean, "foo",
188 (Object[]) null, (Class<?>[]) null));
189 assertEquals("foo(String)", MethodUtils.invokeExactMethod(testBean,
190 "foo", ""));
191 assertEquals("foo(Object)", MethodUtils.invokeExactMethod(testBean,
192 "foo", new Object()));
193 assertEquals("foo(Integer)", MethodUtils.invokeExactMethod(testBean,
194 "foo", NumberUtils.INTEGER_ONE));
195 assertEquals("foo(double)", MethodUtils.invokeExactMethod(testBean,
196 "foo", new Object[] { NumberUtils.DOUBLE_ONE },
197 new Class[] { Double.TYPE }));
198
199 try {
200 MethodUtils
201 .invokeExactMethod(testBean, "foo", NumberUtils.BYTE_ONE);
202 fail("should throw NoSuchMethodException");
203 } catch (final NoSuchMethodException e) {
204 }
205 try {
206 MethodUtils
207 .invokeExactMethod(testBean, "foo", NumberUtils.LONG_ONE);
208 fail("should throw NoSuchMethodException");
209 } catch (final NoSuchMethodException e) {
210 }
211 try {
212 MethodUtils.invokeExactMethod(testBean, "foo", Boolean.TRUE);
213 fail("should throw NoSuchMethodException");
214 } catch (final NoSuchMethodException e) {
215 }
216 }
217
218 @Test
219 public void testInvokeStaticMethod() throws Exception {
220 assertEquals("bar()", MethodUtils.invokeStaticMethod(TestBean.class,
221 "bar", (Object[]) ArrayUtils.EMPTY_CLASS_ARRAY));
222 assertEquals("bar()", MethodUtils.invokeStaticMethod(TestBean.class,
223 "bar", (Object[]) null));
224 assertEquals("bar()", MethodUtils.invokeStaticMethod(TestBean.class,
225 "bar", (Object[]) null, (Class<?>[]) null));
226 assertEquals("bar(String)", MethodUtils.invokeStaticMethod(
227 TestBean.class, "bar", ""));
228 assertEquals("bar(Object)", MethodUtils.invokeStaticMethod(
229 TestBean.class, "bar", new Object()));
230 assertEquals("bar(Object)", MethodUtils.invokeStaticMethod(
231 TestBean.class, "bar", Boolean.TRUE));
232 assertEquals("bar(Integer)", MethodUtils.invokeStaticMethod(
233 TestBean.class, "bar", NumberUtils.INTEGER_ONE));
234 assertEquals("bar(int)", MethodUtils.invokeStaticMethod(TestBean.class,
235 "bar", NumberUtils.BYTE_ONE));
236 assertEquals("bar(double)", MethodUtils.invokeStaticMethod(
237 TestBean.class, "bar", NumberUtils.LONG_ONE));
238 assertEquals("bar(double)", MethodUtils.invokeStaticMethod(
239 TestBean.class, "bar", NumberUtils.DOUBLE_ONE));
240
241 try {
242 MethodUtils.invokeStaticMethod(TestBean.class, "does_not_exist");
243 fail("should throw NoSuchMethodException");
244 } catch (final NoSuchMethodException e) {
245 }
246 }
247
248 @Test
249 public void testInvokeExactStaticMethod() throws Exception {
250 assertEquals("bar()", MethodUtils.invokeExactStaticMethod(TestBean.class,
251 "bar", (Object[]) ArrayUtils.EMPTY_CLASS_ARRAY));
252 assertEquals("bar()", MethodUtils.invokeExactStaticMethod(TestBean.class,
253 "bar", (Object[]) null));
254 assertEquals("bar()", MethodUtils.invokeExactStaticMethod(TestBean.class,
255 "bar", (Object[]) null, (Class<?>[]) null));
256 assertEquals("bar(String)", MethodUtils.invokeExactStaticMethod(
257 TestBean.class, "bar", ""));
258 assertEquals("bar(Object)", MethodUtils.invokeExactStaticMethod(
259 TestBean.class, "bar", new Object()));
260 assertEquals("bar(Integer)", MethodUtils.invokeExactStaticMethod(
261 TestBean.class, "bar", NumberUtils.INTEGER_ONE));
262 assertEquals("bar(double)", MethodUtils.invokeExactStaticMethod(
263 TestBean.class, "bar", new Object[] { NumberUtils.DOUBLE_ONE },
264 new Class[] { Double.TYPE }));
265
266 try {
267 MethodUtils.invokeExactStaticMethod(TestBean.class, "bar",
268 NumberUtils.BYTE_ONE);
269 fail("should throw NoSuchMethodException");
270 } catch (final NoSuchMethodException e) {
271 }
272 try {
273 MethodUtils.invokeExactStaticMethod(TestBean.class, "bar",
274 NumberUtils.LONG_ONE);
275 fail("should throw NoSuchMethodException");
276 } catch (final NoSuchMethodException e) {
277 }
278 try {
279 MethodUtils.invokeExactStaticMethod(TestBean.class, "bar",
280 Boolean.TRUE);
281 fail("should throw NoSuchMethodException");
282 } catch (final NoSuchMethodException e) {
283 }
284 }
285
286 @Test
287 public void testGetAccessibleInterfaceMethod() throws Exception {
288 final Class<?>[][] p = { ArrayUtils.EMPTY_CLASS_ARRAY, null };
289 for (final Class<?>[] element : p) {
290 final Method method = TestMutable.class.getMethod("getValue", element);
291 final Method accessibleMethod = MethodUtils.getAccessibleMethod(method);
292 assertNotSame(accessibleMethod, method);
293 assertSame(Mutable.class, accessibleMethod.getDeclaringClass());
294 }
295 }
296
297 @Test
298 public void testGetAccessibleMethodPrivateInterface() throws Exception {
299 final Method expected = TestBeanWithInterfaces.class.getMethod("foo");
300 assertNotNull(expected);
301 final Method actual = MethodUtils.getAccessibleMethod(TestBeanWithInterfaces.class, "foo");
302 assertNull(actual);
303 }
304
305 @Test
306 public void testGetAccessibleInterfaceMethodFromDescription()
307 throws Exception {
308 final Class<?>[][] p = { ArrayUtils.EMPTY_CLASS_ARRAY, null };
309 for (final Class<?>[] element : p) {
310 final Method accessibleMethod = MethodUtils.getAccessibleMethod(
311 TestMutable.class, "getValue", element);
312 assertSame(Mutable.class, accessibleMethod.getDeclaringClass());
313 }
314 }
315
316 @Test
317 public void testGetAccessiblePublicMethod() throws Exception {
318 assertSame(MutableObject.class, MethodUtils.getAccessibleMethod(
319 MutableObject.class.getMethod("getValue",
320 ArrayUtils.EMPTY_CLASS_ARRAY)).getDeclaringClass());
321 }
322
323 @Test
324 public void testGetAccessiblePublicMethodFromDescription() throws Exception {
325 assertSame(MutableObject.class, MethodUtils.getAccessibleMethod(
326 MutableObject.class, "getValue", ArrayUtils.EMPTY_CLASS_ARRAY)
327 .getDeclaringClass());
328 }
329
330 @Test
331 public void testGetAccessibleMethodInaccessible() throws Exception {
332 final Method expected = TestBean.class.getDeclaredMethod("privateStuff");
333 final Method actual = MethodUtils.getAccessibleMethod(expected);
334 assertNull(actual);
335 }
336
337 @Test
338 public void testGetMatchingAccessibleMethod() throws Exception {
339 expectMatchingAccessibleMethodParameterTypes(TestBean.class, "foo",
340 ArrayUtils.EMPTY_CLASS_ARRAY, ArrayUtils.EMPTY_CLASS_ARRAY);
341 expectMatchingAccessibleMethodParameterTypes(TestBean.class, "foo",
342 null, ArrayUtils.EMPTY_CLASS_ARRAY);
343 expectMatchingAccessibleMethodParameterTypes(TestBean.class, "foo",
344 singletonArray(String.class), singletonArray(String.class));
345 expectMatchingAccessibleMethodParameterTypes(TestBean.class, "foo",
346 singletonArray(Object.class), singletonArray(Object.class));
347 expectMatchingAccessibleMethodParameterTypes(TestBean.class, "foo",
348 singletonArray(Boolean.class), singletonArray(Object.class));
349 expectMatchingAccessibleMethodParameterTypes(TestBean.class, "foo",
350 singletonArray(Byte.class), singletonArray(Integer.TYPE));
351 expectMatchingAccessibleMethodParameterTypes(TestBean.class, "foo",
352 singletonArray(Byte.TYPE), singletonArray(Integer.TYPE));
353 expectMatchingAccessibleMethodParameterTypes(TestBean.class, "foo",
354 singletonArray(Short.class), singletonArray(Integer.TYPE));
355 expectMatchingAccessibleMethodParameterTypes(TestBean.class, "foo",
356 singletonArray(Short.TYPE), singletonArray(Integer.TYPE));
357 expectMatchingAccessibleMethodParameterTypes(TestBean.class, "foo",
358 singletonArray(Character.class), singletonArray(Integer.TYPE));
359 expectMatchingAccessibleMethodParameterTypes(TestBean.class, "foo",
360 singletonArray(Character.TYPE), singletonArray(Integer.TYPE));
361 expectMatchingAccessibleMethodParameterTypes(TestBean.class, "foo",
362 singletonArray(Integer.class), singletonArray(Integer.class));
363 expectMatchingAccessibleMethodParameterTypes(TestBean.class, "foo",
364 singletonArray(Integer.TYPE), singletonArray(Integer.TYPE));
365 expectMatchingAccessibleMethodParameterTypes(TestBean.class, "foo",
366 singletonArray(Long.class), singletonArray(Double.TYPE));
367 expectMatchingAccessibleMethodParameterTypes(TestBean.class, "foo",
368 singletonArray(Long.TYPE), singletonArray(Double.TYPE));
369 expectMatchingAccessibleMethodParameterTypes(TestBean.class, "foo",
370 singletonArray(Float.class), singletonArray(Double.TYPE));
371 expectMatchingAccessibleMethodParameterTypes(TestBean.class, "foo",
372 singletonArray(Float.TYPE), singletonArray(Double.TYPE));
373 expectMatchingAccessibleMethodParameterTypes(TestBean.class, "foo",
374 singletonArray(Double.class), singletonArray(Double.TYPE));
375 expectMatchingAccessibleMethodParameterTypes(TestBean.class, "foo",
376 singletonArray(Double.TYPE), singletonArray(Double.TYPE));
377 expectMatchingAccessibleMethodParameterTypes(TestBean.class, "foo",
378 singletonArray(Double.TYPE), singletonArray(Double.TYPE));
379 expectMatchingAccessibleMethodParameterTypes(InheritanceBean.class, "testOne",
380 singletonArray(ParentObject.class), singletonArray(ParentObject.class));
381 expectMatchingAccessibleMethodParameterTypes(InheritanceBean.class, "testOne",
382 singletonArray(ChildObject.class), singletonArray(ParentObject.class));
383 expectMatchingAccessibleMethodParameterTypes(InheritanceBean.class, "testTwo",
384 singletonArray(ParentObject.class), singletonArray(GrandParentObject.class));
385 expectMatchingAccessibleMethodParameterTypes(InheritanceBean.class, "testTwo",
386 singletonArray(ChildObject.class), singletonArray(ChildInterface.class));
387 }
388
389 @Test
390 public void testNullArgument() {
391 expectMatchingAccessibleMethodParameterTypes(TestBean.class, "oneParameter",
392 singletonArray(null), singletonArray(String.class));
393 }
394
395 @Test
396 public void testGetOverrideHierarchyIncludingInterfaces() {
397 final Method method = MethodUtils.getAccessibleMethod(StringParameterizedChild.class, "consume", String.class);
398 final Iterator<MethodDescriptor> expected =
399 Arrays.asList(new MethodDescriptor(StringParameterizedChild.class, "consume", String.class),
400 new MethodDescriptor(GenericParent.class, "consume", GenericParent.class.getTypeParameters()[0]),
401 new MethodDescriptor(GenericConsumer.class, "consume", GenericConsumer.class.getTypeParameters()[0]))
402 .iterator();
403 for (final Method m : MethodUtils.getOverrideHierarchy(method, Interfaces.INCLUDE)) {
404 assertTrue(expected.hasNext());
405 final MethodDescriptor md = expected.next();
406 assertEquals(md.declaringClass, m.getDeclaringClass());
407 assertEquals(md.name, m.getName());
408 assertEquals(md.parameterTypes.length, m.getParameterTypes().length);
409 for (int i = 0; i < md.parameterTypes.length; i++) {
410 assertTrue(TypeUtils.equals(md.parameterTypes[i], m.getGenericParameterTypes()[i]));
411 }
412 }
413 assertFalse(expected.hasNext());
414 }
415
416 @Test
417 public void testGetOverrideHierarchyExcludingInterfaces() {
418 final Method method = MethodUtils.getAccessibleMethod(StringParameterizedChild.class, "consume", String.class);
419 final Iterator<MethodDescriptor> expected =
420 Arrays.asList(new MethodDescriptor(StringParameterizedChild.class, "consume", String.class),
421 new MethodDescriptor(GenericParent.class, "consume", GenericParent.class.getTypeParameters()[0]))
422 .iterator();
423 for (final Method m : MethodUtils.getOverrideHierarchy(method, Interfaces.EXCLUDE)) {
424 assertTrue(expected.hasNext());
425 final MethodDescriptor md = expected.next();
426 assertEquals(md.declaringClass, m.getDeclaringClass());
427 assertEquals(md.name, m.getName());
428 assertEquals(md.parameterTypes.length, m.getParameterTypes().length);
429 for (int i = 0; i < md.parameterTypes.length; i++) {
430 assertTrue(TypeUtils.equals(md.parameterTypes[i], m.getGenericParameterTypes()[i]));
431 }
432 }
433 assertFalse(expected.hasNext());
434 }
435
436 @Test
437 @Annotated
438 public void testGetMethodsWithAnnotation() throws NoSuchMethodException {
439 assertArrayEquals(new Method[0], MethodUtils.getMethodsWithAnnotation(Object.class, Annotated.class));
440
441 Method[] methodsWithAnnotation = MethodUtils.getMethodsWithAnnotation(MethodUtilsTest.class, Annotated.class);
442 assertEquals(2, methodsWithAnnotation.length);
443 assertThat(methodsWithAnnotation, hasItemInArray(MethodUtilsTest.class.getMethod("testGetMethodsWithAnnotation")));
444 assertThat(methodsWithAnnotation, hasItemInArray(MethodUtilsTest.class.getMethod("testGetMethodsListWithAnnotation")));
445 }
446
447 @Test(expected = IllegalArgumentException.class)
448 public void testGetMethodsWithAnnotationIllegalArgumentException1() {
449 MethodUtils.getMethodsWithAnnotation(FieldUtilsTest.class, null);
450 }
451
452 @Test(expected = IllegalArgumentException.class)
453 public void testGetMethodsWithAnnotationIllegalArgumentException2() {
454 MethodUtils.getMethodsWithAnnotation(null, Annotated.class);
455 }
456
457 @Test(expected = IllegalArgumentException.class)
458 public void testGetMethodsWithAnnotationIllegalArgumentException3() {
459 MethodUtils.getMethodsWithAnnotation(null, null);
460 }
461
462 @Test
463 @Annotated
464 public void testGetMethodsListWithAnnotation() throws NoSuchMethodException {
465 assertEquals(0, MethodUtils.getMethodsListWithAnnotation(Object.class, Annotated.class).size());
466
467 final List<Method> methodWithAnnotation = MethodUtils.getMethodsListWithAnnotation(MethodUtilsTest.class, Annotated.class);
468 assertEquals(2, methodWithAnnotation.size());
469 assertThat(methodWithAnnotation, hasItems(
470 MethodUtilsTest.class.getMethod("testGetMethodsWithAnnotation"),
471 MethodUtilsTest.class.getMethod("testGetMethodsListWithAnnotation")
472 ));
473 }
474
475 @Test(expected = IllegalArgumentException.class)
476 public void testGetMethodsListWithAnnotationIllegalArgumentException1() {
477 MethodUtils.getMethodsListWithAnnotation(FieldUtilsTest.class, null);
478 }
479
480 @Test(expected = IllegalArgumentException.class)
481 public void testGetMethodsListWithAnnotationIllegalArgumentException2() {
482 MethodUtils.getMethodsListWithAnnotation(null, Annotated.class);
483 }
484
485 @Test(expected = IllegalArgumentException.class)
486 public void testGetMethodsListWithAnnotationIllegalArgumentException3() {
487 MethodUtils.getMethodsListWithAnnotation(null, null);
488 }
489
490 private void expectMatchingAccessibleMethodParameterTypes(final Class<?> cls,
491 final String methodName, final Class<?>[] requestTypes, final Class<?>[] actualTypes) {
492 final Method m = MethodUtils.getMatchingAccessibleMethod(cls, methodName,
493 requestTypes);
494 assertTrue(toString(m.getParameterTypes()) + " not equals "
495 + toString(actualTypes), Arrays.equals(actualTypes, m
496 .getParameterTypes()));
497 }
498
499 private String toString(final Class<?>[] c) {
500 return Arrays.asList(c).toString();
501 }
502
503 private Class<?>[] singletonArray(final Class<?> c) {
504 Class<?>[] result = classCache.get(c);
505 if (result == null) {
506 result = new Class[] { c };
507 classCache.put(c, result);
508 }
509 return result;
510 }
511
512 public static class InheritanceBean {
513 public void testOne(final Object obj) {}
514 public void testOne(final GrandParentObject obj) {}
515 public void testOne(final ParentObject obj) {}
516 public void testTwo(final Object obj) {}
517 public void testTwo(final GrandParentObject obj) {}
518 public void testTwo(final ChildInterface obj) {}
519 }
520
521 interface ChildInterface {}
522 public static class GrandParentObject {}
523 public static class ParentObject extends GrandParentObject {}
524 public static class ChildObject extends ParentObject implements ChildInterface {}
525
526 private static class MethodDescriptor {
527 final Class<?> declaringClass;
528 final String name;
529 final Type[] parameterTypes;
530
531 MethodDescriptor(final Class<?> declaringClass, final String name, final Type... parameterTypes) {
532 this.declaringClass = declaringClass;
533 this.name = name;
534 this.parameterTypes = parameterTypes;
535 }
536 }
537 }